package zom.zenjava.zendeploy.util;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import zom.zenjava.zendeploy.model.ApplicationProfile;
import zom.zenjava.zendeploy.model.Dependency;
import zom.zenjava.zendeploy.model.DependencySet;
import zom.zenjava.zendeploy.model.JreReference;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

public class ApplicationProfileReader
{
    private final static Logger LOGGER = Logger.getLogger(ApplicationProfileReader.class.getName());

    public ApplicationProfile read(File file) throws ApplicationProfileReadException
    {
        try
        {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setIgnoringElementContentWhitespace(true);
            dbf.setIgnoringComments(true);
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(file);

            Node applicationNode = doc.getFirstChild();
            if (!applicationNode.getNodeName().equals("application"))
            {
                String message = String.format(
                        "Invalid AppProfile XML: expected root element 'application' but found '%s'",
                        applicationNode.getNodeName());
                LOGGER.severe(message);
                throw new ApplicationProfileReadException(message);
            }

            String name = null;
            String version = null;
            String url = null;
            String jreVersion = null;
            String jreUrl = null;
            List<DependencySet> dependencySets = new ArrayList<DependencySet>();
            
            NodeList childNodes = applicationNode.getChildNodes();
            for (int i = 0; i < childNodes.getLength(); i++)
            {
                Node child = childNodes.item(i);
                if (child.getNodeName().equals("name"))
                {
                    if (child.getChildNodes().getLength() > 0)
                    {
                        name = child.getFirstChild().getNodeValue();
                        LOGGER.fine(String.format("Application name is '%s'", name));
                    }
                }
                else if (child.getNodeName().equals("version"))
                {
                    if (child.getChildNodes().getLength() > 0)
                    {
                        version = child.getFirstChild().getNodeValue();
                        LOGGER.fine(String.format("Version is '%s'", version));
                    }
                }
                else if (child.getNodeName().equals("url"))
                {
                    if (child.getChildNodes().getLength() > 0)
                    {
                        url = child.getFirstChild().getNodeValue();
                        LOGGER.fine(String.format("URL is '%s'", url));
                    }
                }
                else if (child.getNodeName().equals("jre"))
                {
                    NodeList jreAttributes = child.getChildNodes();
                    for (int j = 0; j < jreAttributes.getLength(); j++)
                    {
                        Node jreAttribute = jreAttributes.item(j);
                        if (jreAttribute.getNodeName().equals("version"))
                        {
                            if (jreAttribute.getChildNodes().getLength() > 0)
                            {
                                jreVersion = jreAttribute.getFirstChild().getNodeValue();
                                LOGGER.fine(String.format("JRE Version is '%s'", jreVersion));
                            }
                        }
                        else if (jreAttribute.getNodeName().equals("url"))
                        {
                            if (jreAttribute.getChildNodes().getLength() > 0)
                            {
                                jreUrl = jreAttribute.getFirstChild().getNodeValue();
                                LOGGER.fine(String.format("JRE URL is '%s'", jreUrl));
                            }
                        }
                        else
                        {
                            LOGGER.warning(String.format("Skipping unrecognized JRE attribute of type '%s'", jreAttribute.getNodeName()));
                        }
                    }
                }
                else if (child.getNodeName().equals("dependencies"))
                {
                    NamedNodeMap attributes = child.getAttributes();
                    Node baseUrlNode = attributes.getNamedItem("baseUrl");
                    String baseUrl = baseUrlNode != null ? baseUrlNode.getNodeValue() : null;
                    List<Dependency> dependencies = new ArrayList<Dependency>();

                    NodeList dependencyNodes = child.getChildNodes();
                    for (int d = 0; d < dependencyNodes.getLength(); d++)
                    {
                        Node dependencyNode = dependencyNodes.item(d);
                        if (dependencyNode.getNodeName().equals("jar"))
                        {
                            attributes = dependencyNode.getAttributes();
                            String jarId = attributes.getNamedItem("id").getNodeValue();
                            String jarVersion = attributes.getNamedItem("version").getNodeValue();
                            String jarPath = attributes.getNamedItem("path").getNodeValue();
                            dependencies.add(new Dependency(jarId, jarVersion, jarPath));
                        }
                        else
                        {
                            LOGGER.warning(String.format("Skipping unrecognized dependency of type '%s'", child.getNodeName()));
                        }
                    }

                    dependencySets.add(new DependencySet(baseUrl, dependencies));
                }
                else
                {
                    LOGGER.warning(String.format("Skipping unrecognized element  '%s'", child.getNodeName()));
                }
            }

            return new ApplicationProfile(name, version, url, new JreReference(jreVersion, jreUrl), dependencySets);
        }
        catch (ParserConfigurationException e)
        {
            throw new ApplicationProfileReadException("XML Parser is not configured properly", e);
        }
        catch (SAXException e)
        {
            throw new ApplicationProfileReadException("Error parsing XML file" + file.getAbsolutePath(), e);
        }
        catch (IOException e)
        {
            throw new ApplicationProfileReadException("Error reading App Profile: " + file.getAbsolutePath(), e);
        }
    }
}
